package com.c2soft.ncmx.exch.service.engine;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;

import com.c2soft.ncmx.exch.dao.TradeMgmtDAO;
import com.c2soft.ncmx.exch.pojo.OrderBook;
import com.c2soft.ncmx.exch.pojo.OrderBookEntry;
import com.c2soft.ncmx.exch.pojo.OrderBookEntry.Type;

import com.c2soft.ncmx.exch.exceptions.NcmxBussinessException;


public  class GenericTradeEngine {
	
	@Autowired
	private TradeMgmtDAO tradeMgmtDAO;
    
	private  final long DPR_MIN = 900000;
	
    private  final long DPR_MAX = 1100000;
	
	private long askMin =DPR_MAX;
	
	private long bidMax=DPR_MIN;
	
	private  final long tickPrice = 100;
	
    private List<OrderBook> priceQueueList = null;
    
    private void initiatePriceQueueList(){
    	int arraylength =  (int) ((DPR_MAX-DPR_MIN)/tickPrice);
        priceQueueList =  new ArrayList<OrderBook>(arraylength);
    	
        for(int i=0; i<arraylength;i++)
    	{
    		priceQueueList.add(new OrderBook());
    	}
    }
	
	private void insertOrder(OrderBookEntry order){
		//OrderBook orderBook=null;
		int arrayIndex = getArrayIndex(order.getPrice());
		//if(priceQueueList.size()!=0){
		OrderBook orderBook =  priceQueueList.get(arrayIndex);
		//}
		
		if(orderBook.getOrderBookList() != null)
		{
			orderBook.addElementOrderBookList(order);
			orderBook.setTotalQuantity(orderBook.getTotalQuantity() + order.getQty());
		}
		else
		{
			orderBook = new OrderBook();
			orderBook.setOrderBookList(orderBook.createOrderBookList());
			orderBook.addElementOrderBookList(order);
			orderBook.setTotalQuantity(order.getQty());
			priceQueueList.add(arrayIndex,orderBook);
		}
	}
	
	private int getArrayIndex(long price){
		if((price<DPR_MIN || price>(DPR_MAX))){	
		throw new NcmxBussinessException("BAD price!!!");
		}
		return (int) ((price - DPR_MIN)/tickPrice);
	}
	
	
	
	public String processOrder(OrderBookEntry order){
		
		
		String currentOrderId = order.getOrderId();
		OrderBook orderBook;
		int arrayIndex=0;
		
		switch (order.getType()) {
		case BUY:
			arrayIndex = getArrayIndex(askMin);
			if(priceQueueList!=null){
			orderBook = priceQueueList.get(arrayIndex);
			processBuyOrder(order, orderBook);
			}else{
				initiatePriceQueueList();
				insertOrder(order);
			}
			
			
			break;

		case SELL:
			arrayIndex = getArrayIndex(askMin);
			if(priceQueueList!=null){
			orderBook = priceQueueList.get(arrayIndex);
			processSellOrder(order);
			}else{
				initiatePriceQueueList();
				insertOrder(order);
			}
			break;
		
		case CANCEL:
			processCancelOrder(order);
			break;
		
		default:
			throw new IllegalArgumentException();
		}
		
		return currentOrderId;
	}
	
	private void processBuyOrder(OrderBookEntry currOrder, OrderBook sellOrderBook){
	
		long bidPrice =  currOrder.getPrice();
		int bidQuantity =   currOrder.getQty();
		if(bidPrice>=DPR_MAX)
		{
		throw new NcmxBussinessException("Bad BID Price!!! for order: "+currOrder.getOrderId());	
		
		}
		if(bidPrice >= askMin )
		{
			do
			{

				List<OrderBookEntry> sellOrderBookList = sellOrderBook.getOrderBookList();
				Iterator<OrderBookEntry> sellOrderBookListIterator = sellOrderBookList.iterator();

				OrderBookEntry bookEntry;
				int index =-1;
				while(sellOrderBookListIterator.hasNext())
				{
					index++;
					bookEntry = sellOrderBookListIterator.next();
					
					if(bookEntry.getQty() < bidQuantity){
						executeTrade(currOrder,bookEntry,bidPrice,bookEntry.getQty());
						bidQuantity -= bookEntry.getQty();
						sellOrderBookListIterator.remove();
					}else
					{
						executeTrade(currOrder,bookEntry,bidPrice,bidQuantity);
						if(bookEntry.getQty() > bidQuantity){
							bookEntry.setQty(bookEntry.getQty()-bidQuantity);
							sellOrderBook.set(index, bookEntry);
						}
						else
						{ 
							//both quantity are equal in this case
							sellOrderBookListIterator.remove();
						}
						
						return;
					}
				}
		
			   askMin = askMin + tickPrice;
				
			}while(bidPrice >= askMin);
			
		}
		OrderBookEntry newEntry = new OrderBookEntry("BUY", currOrder.getOrderId(), bidPrice, bidQuantity);
		insertOrder(newEntry);
		if(bidMax < bidPrice)
		{
			bidMax = bidPrice;
		}
	}
	
	private void processSellOrder(OrderBookEntry currOrder){
		
		long askPrice =  currOrder.getPrice();;
		int askQuantity =   currOrder.getQty();
	
		if(askPrice <= bidMax)
		{
			do
			{
				
				int arrayIndex = getArrayIndex(bidMax);

				List<OrderBookEntry> buyOrderBook =  (List<OrderBookEntry>) priceQueueList.get(arrayIndex);
					
				Iterator<OrderBookEntry> buyOrderBookIterator = buyOrderBook.iterator();

				OrderBookEntry bookEntry;
				
				int index = -1;
				while(buyOrderBookIterator.hasNext())
				{
					index++;
					bookEntry = buyOrderBookIterator.next();
					
					if(bookEntry.getQty() < askQuantity){
						executeTrade(bookEntry,currOrder,askPrice,bookEntry.getQty());
						//executeTrade(askPrice);
						askQuantity -= bookEntry.getQty();
						buyOrderBookIterator.remove();
					}else
					{
						executeTrade(bookEntry,currOrder,askPrice,askQuantity);
						//executeTrade(askPrice);
						if(bookEntry.getQty() > askQuantity){
							bookEntry.setQty(bookEntry.getQty()-askQuantity);
							buyOrderBook.set(index, bookEntry);
						}
						else
						{ 
							//both quantity are equal in this case
							buyOrderBookIterator.remove();
						}
						
						return;
					}
				}
		
			   bidMax = bidMax - tickPrice;
				
			}while(askPrice <= bidMax);
			
		}
		OrderBookEntry entry = new OrderBookEntry("BUY", currOrder.getOrderId(), askPrice, askQuantity);
		insertOrder(entry);
		if(askMin > askPrice)
		{
			askMin = askPrice;
		}
	}
	
	private void processCancelOrder(OrderBookEntry order){
		int arrayIndex = getArrayIndex(askMin);
		List<OrderBookEntry> cancelOrderBook =  (List<OrderBookEntry>) priceQueueList.get(arrayIndex);
		Iterator<OrderBookEntry> cancelOrderBookIterator = cancelOrderBook.iterator();

		OrderBookEntry cancelOrderEntry;
		String orderId = order.getOrderId();
		while(cancelOrderBookIterator.hasNext())
		{
			cancelOrderEntry = cancelOrderBookIterator.next();
			if(cancelOrderEntry.getOrderId() != null && cancelOrderEntry.getOrderId().equals(orderId))
			{
				//need to have partially executed order support
				cancelOrderBookIterator.remove();
			}
		}
			
	}
	
	private void executeTrade(OrderBookEntry buyOrder,OrderBookEntry sellOrder,long price,int qty){	
		tradeMgmtDAO.saveTradeService(buyOrder, sellOrder, price, qty);
	}
	
	public static void main(String[] args) {
		// TODO Auto-generated method stub

	}

}
